package glue.ftp;

import java.io.BufferedReader;
import java.io.BufferedInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.io.InputStreamReader;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.OutputStreamWriter;
import java.io.RandomAccessFile;
import java.io.UnsupportedEncodingException;
import java.net.Socket;
import java.net.ServerSocket;
import java.net.SocketException;
import java.util.ResourceBundle;
import java.util.MissingResourceException;
import java.util.StringTokenizer;
import java.util.Vector;
import java.io.InputStream;
//import glue.ftp.*;
import java.io.Serializable;
/*
 * FtpBean			Version 1.4.4
 * Copyright 1999 Calvin Tai
 * E-mail: calvin_tai2000@yahoo.com.hk
 * URL: http://www.geocities.com/SiliconValley/Code/9129/javabean/ftpbean
 *
 * COPYRIGHT NOTICE
 * Copyright 1999 Calvin Tai All Rights Reserved.
 *
 * FtpBean may be modified and used in any application free of charge by
 * anyone so long as this copyright notice and the comments above remain
 * intact. By using this code you agree to indemnify Calvin Tai from any
 * liability that might arise from it's use.
 *
 * Selling the code for this java bean alone is expressly forbidden.
 * In other words, please ask first before you try and make money off of 
 * this java bean as a standalone application. 
 *
 * Obtain permission before redistributing this software over the Internet or
 * in any other medium.  In all cases copyright and header must remain intact.
 */
/*
* All I did was take out the thread stafty stuff. If you're looking to this to learn ftp,
* let me suggest you go back to the original.
*
*/
public class FtpBean implements Serializable
{
    protected final static String FTP_INIT = "FTP_INIT";
    // Ftp commands set
    protected final static String CMD_ACCT = "ACCT ";
    protected final static String CMD_APPE = "APPE ";
    protected final static String CMD_CWD  = "CWD ";
    protected final static String CMD_CDUP = "CDUP";
    protected final static String CMD_DELE = "DELE ";
    protected final static String CMD_MKD  = "MKD ";
    protected final static String CMD_PASV = "PASV";
    protected final static String CMD_PASS = "PASS ";
    protected final static String CMD_PORT = "PORT ";
    protected final static String CMD_PWD  = "PWD";
    protected final static String CMD_QUIT = "QUIT";
    protected final static String CMD_RMD  = "RMD ";
    protected final static String CMD_REST = "REST ";
    protected final static String CMD_RETR = "RETR ";
    protected final static String CMD_RNTO = "RNTO ";
    protected final static String CMD_RNFR = "RNFR ";
    protected final static String CMD_SITE = "SITE ";
    protected final static String CMD_STOR = "STOR ";
    protected final static String CMD_SYST = "SYST";
    protected final static String CMD_USER = "USER ";
    protected final static String CMD_TYPE = "TYPE ";
    protected final static String CMD_LIST = "LIST";

    // Reply code type, determined by the first digit of the reply code
    protected final static String REPLY_POS_PRE = "1";	// Positive Preliminary Reply
    protected final static String REPLY_POS_CMP = "2";	// Positive Completion Reply
    protected final static String REPLY_POS_INT = "3";	// Positive Intermediate Reply
    protected final static String REPLY_TRA_NEG = "4";	// Transient Negative Completion Reply
    protected final static String REPLY_PER_NEG = "5";	// Permanent Negative Completion Reply
    protected final static String REPLY_UNDEF   = "0";	// Undefined reply, should not be exist.

    private final String TF_MOD_ASCII = "A";
    private final String TF_MOD_BIN   = "I";

    private final String FTP_ENCODING = "US-ASCII";

    private final FtpReplyResourceBundle ftpReplies = new FtpReplyResourceBundle();

    private String acctInfo = "";          // account information
    private String server = "";            // server name
    private String user = "";              // user name
    private String replymessage = "";      // reply message from server
    private String reply = "";             // reply to the command
    private Socket socket = null;          // Socket for FTP connection
    private BufferedReader in;             // Input for FTP connection
    private PrintWriter out;               // Output for FTP connection
    private int port = 21;                 // FTP port number, default 21
    private boolean passive = true;        // Passive mode transfer, default true
    private int timeout;                   // Timeout to open socket

    // Needed for thread safety
//    private int[] lock = new int[0];               // For synchronized locking
    private boolean acquired = false;              // Acquired by a thread or not
    private Vector thread_spool = new Vector();    // Spool for the waiting threads

    // Needed for some Visual tools
//    private PropertyChangeSupport pcs;     // PropertyChangeSupport for visual tools

    final private boolean DEBUG = false;   // True to turn on debug mode
	static int maxConnections = 0;
	static boolean maxConnectionsReached = false;
	static int currentConnections = 0;
	boolean bConnected = false;
	public FtpBean()
	{
	}
	
	public boolean canAddConnection(){
		System.out.println("canAddConnection current:" + currentConnections + " max:" + maxConnections);
		if(!maxConnectionsReached){
			return true;
		}else{
			if((currentConnections < maxConnections)){
				return true;
			}else{
				return false;
			}
		}
	}
    /**
     * Connect to Ftp server and login.
     * @param server Name of server
     * @param user User name for login
     * @exception FtpException if a ftp error occur (eg. Login fail in this case).
     * @exception IOException if an I/O error occur
     */
    public boolean ftpConnect(String server, String user)
        throws IOException, FtpException
    {
		return ftpConnect(server, user, "", "");
    }

    /**
     * Connect to Ftp server and login.
     * @param server Name of server
     * @param user User name for login
     * @param password Password for login
     * @exception FtpException if a ftp error occur (eg. Login fail in this case).
     * @exception IOException if an I/O error occur
     */
    public boolean ftpConnect(String server, String user, String password)
        throws IOException, FtpException
    {
		return ftpConnect(server, user, password, "");
    }
    
    /**
     * Connect to FTP server and login.
     * @param server Name of server
     * @param user User name for login
     * @param password Password for login
     * @param acct account information
     * @exception FtpException if a ftp error occur (eg. Login fail in this case).
     * @exception IOException if an I/O error occur
     */
    public boolean ftpConnect(String server, String user, String password, String acct)
        throws IOException, FtpException
    {
//	    System.out.println("ftpConnect:" + user);
        if(DEBUG)    // Debug message
            System.out.println("FtpBean: Connecting to server " + server);

//        acquire();   // Acquire the object

        // Set server name & user name
        setServerName(server);
//	    System.out.println("setServerName:" + user);
        setUserName(user);
//	    System.out.println("setUserName:" + user);

        // Create socket, get input & output stream
//        try
//        {
//			socket = null;
			
            if (timeout == 0){
//	            System.out.println("timeout == 0");
                socket = new Socket(server, port);
                if(socket == null)
                	System.out.println("socket == null");
//	    		System.out.println("socket:" + user + " socket:" + socket);
            }else{
	            System.out.println("timeout != 0");
                /* If a timeout has been set before opening the socket,
                 * use the SocketOpener. Note that if the thread times out,
                 * it is forcibly terminated.
                 */
                 socket = new SocketOpener (server, port).makeSocket(timeout);
            }
            
			InputStream s = null;
			s = socket.getInputStream();
//			System.out.println("s:" + s.toString());
            in = new BufferedReader(
                     new InputStreamReader(s, FTP_ENCODING));
//            System.out.println("in:" + in + " user:" + user);
            out = new PrintWriter(
	              new OutputStreamWriter(socket.getOutputStream(), FTP_ENCODING), true);
//			System.out.println("out:" + out + " user:" + user);
            // Read reply code when get connected
            
            try{
            	getRespond(FTP_INIT);
            }catch(Exception e){
	            maxConnectionsReached = true;
		        in.close();
		        out.close();
//		        socket.close();
		        in = null;
		        out = null;
//		        socket = null;
	            // Set account information, server name & user name to ""
	            setServerName("");
	            setUserName("");
	            
//	            System.out.println("does it get here?");
	            return false;
//	            ftpConnect(server, user, password, acct);
	        }

            if(!maxConnectionsReached)
           	 	maxConnections++;
           	 	
           	currentConnections++;
           	
           	bConnected = true;
            
            if(DEBUG)    // Debug message
                System.out.println("FtpBean: Connected");

            // Login
            ftpLogin(user, password, acct);        // check if login success
//			System.out.println("ftpLogin:" + user);
//        }finally{
//            release();    // Release the object
//        }
//	    System.out.println("end ftpConnect:" + user);
		return true;
    }

    /**
     * Close FTP connection.
     * @exception IOException if an I/O error occur
     * @exception FtpException if a ftp error occur
     */
    public void close()
        throws IOException, FtpException
    {
//        acquire();    // Acquire the object

        try
        {
        	currentConnections--;
	        if(bConnected)
          	  ftpCommand(CMD_QUIT);

            closeSocket();
            // Set account information, server name & user name to ""
            setServerName("");
            setUserName("");
//            setAcctInfo("");
        }finally
        {
//            release();    // Release the object
        }
        
    }    
    /*
     * Login to server, using FTP commands "USER" and "PASS"
     * @param user FTP username
     * @param password FTP Password
     */
    private void ftpLogin(String user, String password, String acct)
        throws IOException, FtpException
    {
//	    System.out.println("ftpLogin");
        ftpCommand(CMD_USER ,user);        // send user name
		if(getReplyType(reply).equals(REPLY_POS_CMP)){
//			System.out.println("user REPLY_POS_CMP");
	    	return;
		}
        ftpCommand(CMD_PASS, password);    // send password
        
		if(getReplyType(reply).equals(REPLY_POS_CMP)){
//			System.out.println("password REPLY_POS_CMP");
	    	return;
		}
		ftpCommand(CMD_ACCT, acct);
//	    System.out.println("end ftpLogin");
    }
    
    /*
     * Get the reply type by identifying the first digit of the reply code.
     */
    private String getReplyType(String reply_code)
    {
//	    System.out.println("getReplyType: " + user);
		if(reply_code == null || reply_code.length() <= 0){
//		    System.out.println("getReplyType: REPLY_UNDEF" + " user:" + user);
	    	return REPLY_UNDEF;
		}
	    	
		String reply_type = reply_code.substring(0, 1);
		if(!(reply_type.equals(REPLY_POS_PRE) || 
		     reply_type.equals(REPLY_POS_CMP) || 
		     reply_type.equals(REPLY_POS_INT) ||
		     reply_type.equals(REPLY_TRA_NEG) ||
		     reply_type.equals(REPLY_PER_NEG))){
//			  	System.out.println("getReplyType: REPLY_UNDEF"+ " user:"  + user);   
		    	return REPLY_UNDEF;
		     }
//		System.out.println("getReplyType:" + reply_type + " user:" + user);     
		return reply_type;
    }
    
    
    /* 
     * Send FTP command to the server.
     * @param command The command to be sent
     */
    private void ftpCommand(String cmd)
        throws IOException, FtpException
    {
		ftpCommand(cmd, "");
    }
    
    
    
    /* 
     * Send FTP command to the server.
     * @param command The command to be sent
     * @param Expected code return from the command.
     */
    private void ftpCommand(String cmd, String param)
        throws IOException, FtpException
    {
//	    System.out.println("ftpCommand");
        if(out == null){
//	        System.out.println("out == null");
            return;
        }
//	    System.out.println("out != null");

        if(DEBUG)    // Debug message
        {
            if(cmd.equals(CMD_PASS))
                System.out.println("FtpBean: Send password");
            else
                System.out.println("FtpBean: Send command \"" + cmd + param + "\"");
        }

        out.print(cmd + param + "\r\n");    // Send cmd
        out.flush();

        getRespond(cmd);
//	    System.out.println("end ftpCommand");
    }
    
    /*
     * Read the respond message from the server's inputstream and assign to replymessage
     */
    private void getRespond(String cmd)
        throws IOException, FtpException
    {
//	    System.out.println("getRespond:" + user);
	    boolean test = false;
        String line = "";
        String replymessage = "";
//        System.out.println("enter loop:" + user);
        do
        {
            line = in.readLine();
//	    	System.out.println("line:" + line + " ***user:" + user);
            if(!checkReply(line)){
                break;
            }
            replymessage = replymessage.concat(line).concat("\n");
//	    	System.out.println("looping:" + user);
        } while(true);
//        System.out.println("replymessage:" + replymessage + " ***user:" + user);
//        System.out.println("exit loop:" + user);
        
        setReplyMessage(replymessage);
 //       if(test)
 //       	System.out.println("setReplyMessage:" + user);
        setReply(line);
//        if(test)
//	        System.out.println("setReply:" + user);
        
		String reply_type = getReplyType(reply);
//        System.out.println("getReplyType:" + reply_type + " user:" + user);
		String[] valid_replies = null;
		
		try { 
			valid_replies = ftpReplies.getStringArray(cmd); 
//			if(test)
//	        	System.out.println("valid_replies:" + valid_replies + " user:" + user);
		}catch(MissingResourceException e) { 
//			System.out.println("FtpException:" + e + " user:" + user);
			throw new FtpException("Valid reply for command '" + cmd + "' not found in reply resource bundle"); 
		}
		
		boolean valid = false;
		for(int i = 0; i < valid_replies.length; i++)
		{
		    if(reply_type.equals(valid_replies[i]))
		    {
				valid = true;
				break;
		    }
		}
		
/*		if(test){
			System.out.println("valid:" + valid);
		}
*/		
		if(!valid)
		    throw new FtpException(reply);
//		System.out.println("end ftpRespond:" + user);
	}
	
    /*
     * Check the input string is a reply code or not
     */
    private boolean checkReply(String str)
    {
		if(str == null)
		    return true;
	        // Return true if the fourth character is a space.
        if(str.length() > 3 &&
           str.charAt(3) == ' ' &&
           Character.isDigit(str.charAt(0)) &&
           Character.isDigit(str.charAt(1)) &&
           Character.isDigit(str.charAt(2))){
            return false;
        }else{
            return true;
        }
    }
	
    
    /*
     * Set reply of the last command
     */
    private void setReply(String reply)
    {
       this.reply = reply;
    }

    /*
     * Set reply message and fire property change
     * @param reply The reply message to be set.
     */
    private void setReplyMessage(String replymessage)
    {
       this.replymessage = replymessage;
    }
	
	
    /*
     * Set server name and fire property change
     * @param server The name of the server.
     */
    private void setServerName(String server)
    {
        this.server = server;
    }
    
    
    
    /*
     * Set user name and fire property change
     */
    private void setUserName(String user)
    {
        this.user = user;
    }

    /*
     * Close the Socket, input and output stream
     */
    private void closeSocket()
        throws IOException
    {
        in.close();
        out.close();
        socket.close();
        in = null;
        out = null;
        socket = null;
    }
 
    /**
     * Save an ascii file to the server.
     * <br>Remark:<br>
     * this method convert the line separator of the String content to <br>
     * NVT-ASCII format line separator "\r\n". Then the ftp daemon will <br>
     * convert the NVT-ASCII format line separator into its system line separator.
     * @param filename The name of file
     * @param content The String content of the file
     * @param separator Line separator of the content
     * @exception FtpException if a ftp error occur. (eg. permission denied in this case)
     * @exception IOException if an I/O error occur.
     */
    public void putAsciiFile(String filename, String content, String separator)
        throws IOException, FtpException
    {
        if(out == null)
            return;

        content = changeLineSeparator(content, separator, "\r\n");
        byte[] byte_content = content.getBytes(FTP_ENCODING);

//        acquire();    // Acquire the object

        try
        {
            setTransferType(true);
            putFile(filename, byte_content, 0);
        } finally
        {
//            release();    // Release the object
        }
    }
    
    /*
     * Replace the line separator of a text
     */
    private String changeLineSeparator(String text , String old_separator, String new_separator)
    {
        if(DEBUG)    // Debug message
            System.out.println("FtpBean: Converting ASCII format");

        if(old_separator.equals(new_separator))
            return text;
        StringBuffer content= new StringBuffer("");
        int index;
        while((index = text.indexOf(old_separator)) != -1)
        {
            content.append(text.substring(0, index)).append(new_separator);
            text = text.substring(index + old_separator.length());
        }
        if(text.length() > 0)
            content.append(text);
        return content.toString();
    }

    
    
    /*
     * Set the transfer type
     * @param ascii True for ascii transfer type
     */
    private void setTransferType(boolean ascii)
        throws IOException, FtpException
    {
        if(ascii) { ftpCommand(CMD_TYPE, TF_MOD_ASCII); }
        else { ftpCommand(CMD_TYPE, TF_MOD_BIN); }
    }
    


    /*
     * Get a file, return a byte array.
     * @param filename Name of binary file
     * @param restart The restarting point
     * @param observer Observer of the downloading progress
     */
    private byte[] getFile(String filename, long restart, FtpObserver observer)
        throws IOException, FtpException
    {
        if(out == null)
            return null;

        Socket sock = null;
        sock = getDataSocket(CMD_RETR, filename, restart);

        // Read bytes from server
        BufferedInputStream reader = new BufferedInputStream(
                        sock.getInputStream());
        byte[] data = getBytes(reader, observer);
        reader.close();
        sock.close();
        getRespond(CMD_RETR);

        if(!reply.substring(0, 3).equals("226"))
        {
            throw new FtpException(reply);    // transfer incomplete
        }
        return data;
    }

    /*
     * Read bytes continuously from the BufferedInputStream.
     * Add the new read bytes to an byte array.
     * Return the byte array after all bytes are read.
     * If the FtpObserver is not null, it invokes its byteRead method
     * every time there are new bytes read.
     */
    private byte[] getBytes(BufferedInputStream reader, FtpObserver observer)
        throws IOException
    {
        ByteArrayOutputStream out = new ByteArrayOutputStream(1024);
        int offset;
        byte[] data = new byte[1024];
        while((offset = reader.read(data)) != -1)
        {
            out.write(data, 0, offset);
            if(observer != null)
                observer.byteRead(offset);
        }
        return out.toByteArray();
    }

    private void appendFile(String filename, byte[] content)
        throws IOException, FtpException
    {
        Socket sock = null;
        sock = getDataSocket(CMD_APPE, filename, 0);

        // Write data to server
        DataOutputStream writer = new DataOutputStream(sock.getOutputStream());
        writer.write(content, 0, content.length);
        writer.close();
        sock.close();

        getRespond(CMD_APPE);       
    }
    
    /*
     * Accept a byte array and filename, then save to server
     * @param file Name of file to be saved.
     * @param content The content of the ASCII file.
     * @param restart Restarting point
     */
    private void putFile(String filename, byte[] content, long restart)
        throws IOException, FtpException
    {
        Socket sock = null;
        sock = getDataSocket(CMD_STOR, filename, restart);

        // Write data to server
        DataOutputStream writer = new DataOutputStream(sock.getOutputStream());
        writer.write(content, 0, content.length);
        writer.close();
        sock.close();

        getRespond(CMD_STOR);
    }




    /*
     * Establish data connection for transfer
     */
    private Socket getDataSocket(String command, long restart)
        throws IOException, FtpException
    {
		return getDataSocket(command, "", restart);
    }

    /*
     * Establish data connection for transfer
     */
    private Socket getDataSocket(String command, String param, long restart)
        throws IOException, FtpException
    {
        Socket sock = null;
        ServerSocket ssock = null;

        // Establish data conncetion using passive or active mode.
        if(passive)
            sock = getPassiveDataSocket();
        else
            ssock = getActiveDataSocket();

        // Send the restart command if it is greater than zero
        if(restart > 0)
            ftpCommand(CMD_REST, Long.toString(restart));

        // Send commands like LIST, RETR and STOR
        // These commands will return 125 or 150 when success.
        ftpCommand(command, param);

        // Get Socket object for active mode.
        if(!passive){
            sock = ssock.accept();
	    	ssock.close();
		}

        return sock;
    }
    
    /*
     * Establish data connection in passive mode using "PASV" command
     * Change the server to passive mode. 
     * by the command "PASV", it will return its address
     * and port number that it will listen to.
     * Create a Socket object to that address and port number.
     * Then return the Socket object.
     */
    private Socket getPassiveDataSocket()
        throws IOException, FtpException
    {
		Socket sock = null;

        ftpCommand(CMD_PASV);

        // array that holds the outputed address and port number.
        String[] address = new String[6];

        // Extract the address & port numbers from the string like
		// 227 Entering Passive Mode (192.168.111.1,220,235)
        reply = reply.substring(reply.indexOf('(') + 1, reply.indexOf(')'));

        // put the 'reply' to the array 'address'
        StringTokenizer t = new StringTokenizer(reply, ",");
        for(int i = 0; i < 6; i++)
            address[i] = t.nextToken();

        // Returned server address
        String SRV_IP = address[0] + '.' + address[1] + '.' + address[2] + '.' + address[3];

        // Get the port number
        // Left shift the first number by 8
        int NEW_PORT = (Integer.parseInt(address[4]) << 8) +
                        Integer.parseInt(address[5]);

        if(DEBUG)
	  	  System.out.println("FtpBean: Extracted Server ip - " + SRV_IP + ", Port Number - " + NEW_PORT);

        // Create a new socket object
		if(timeout == 0)
		{
			sock = new Socket(SRV_IP, NEW_PORT);
		}else{
	 		sock = new SocketOpener(SRV_IP, NEW_PORT).makeSocket(timeout);
		}
	    return sock;
    }

    /*
     * Establish data connection in active mode using "PORT" command.
     * It create a ServerSocket object to listen for a port number in local machine.
     * Use port command to tell the server which port the local machine is listenning.
     * Return the ServerSocket object.
     */
    private ServerSocket getActiveDataSocket()
        throws IOException, FtpException
    {
        int[] port_numbers = new int[6];            // Array that contains 

        // Get ip address of local machine. ip address and port numbers
        String local_address = socket.getLocalAddress().getHostAddress(); 

        // Assign the ip address of local machine to the array.
        StringTokenizer st = new StringTokenizer(local_address, ".");
        for(int i = 0; i < 4; i++)
            port_numbers[i] = Integer.parseInt(st.nextToken());

        ServerSocket ssocket = new ServerSocket(0);  // ServerSocket to listen to a random free port number

        int local_port = ssocket.getLocalPort();     // The port number it is listenning to

        // Assign port numbers the array
        port_numbers[4] = ((local_port & 0xff00) >> 8);
        port_numbers[5] = (local_port & 0x00ff);

        // Send "PORT" command to server
        String port_param = "";
        for(int i = 0; i < port_numbers.length; i++)
        {
            port_param = port_param.concat(String.valueOf(port_numbers[i]));
            if(i < port_numbers.length - 1)
                port_param = port_param.concat(",");
        }
        ftpCommand(CMD_PORT, port_param);

        return ssocket;
    }
    
    /**
     * Get a binary file and return a byte array.
     * @param filename The name of the binary file to be got.
     * @return An array of byte of the content of the binary file.
     * @exception FtpException if a ftp error occur. (eg. No such file in this case)
     * @exception IOException if an I/O error occur.
     */
    public byte[] getBinaryFile(String filename)
        throws IOException, FtpException
    {
        if(out == null)
            return null;
        return getBinaryFile(filename, 0, null);
    }

    /**
     * Get a binary file and return a byte array.
     * @param filename The name of the binary file to be got.
     * @param observer The observer of the downloading progress.
     * @return An array of byte of the content of the binary file.
     * @exception FtpException if a ftp error occur. (eg. No such file in this case)
     * @exception IOException if an I/O error occur.
     */
    public byte[] getBinaryFile(String filename, FtpObserver observer)
        throws IOException, FtpException
    {
        if(out == null)
            return null;
        return getBinaryFile(filename, 0, observer);
    }

    /**
     * Get a binary file at a restarting point.
     * Return null if restarting point is less than zero.
     * @param filename Name of binary file to be getted.
     * @param restart Restarting point, ignored if less than or equal to zero.
     * @return An array of byte of the content of the binary file.
     * @exception FtpException if a ftp error occur. (eg. No such file in this case)
     * @exception IOException if an I/O error occur.
     */
    public byte[] getBinaryFile(String filename, long restart)
        throws IOException, FtpException
    {
        if(out == null)
            return null;
        return getBinaryFile(filename, restart, null);
    }

    /**
     * Get a binary file at a restarting point.
     * Return null if restarting point is less than zero.
     * @param filename Name of binary file to be getted.
     * @param restart Restarting point, ignored if less than or equal to zero.
     * @param observer The FtpObserver which monitor this downloading progress
     * @return An array of byte of the content of the binary file.
     * @exception FtpException if a ftp error occur. (eg. No such file in this case)
     * @exception IOException if an I/O error occur.
     * @see FtpObserver
     */
    public byte[] getBinaryFile(String filename, long restart, FtpObserver observer)
        throws IOException, FtpException
    {
        if(out == null)
            return null;
        byte[] content;
//        acquire();    // Acquire the object

        try
        {
            setTransferType(false);
            content = getFile(filename, restart, observer);
        } finally
        {
//            release();    // Release the object
        }
        return content;
    }

    /**
     * Read file from ftp server and write to a file in local hard disk.
     * This method is much faster than those method which return a byte array<br>
     * if the network is fast enough.<br>
     * <br>Remark:<br>
     * Cannot be used in unsigned applet.
     * @param ftpfile Name of file to be get from the ftp server, can be in full path.
     * @param localfile Name of local file to be write, can be in full path.
     * @exception FtpException if a ftp error occur. (eg. No such file in this case)
     * @exception IOException if an I/O error occur.
     */
    public void getBinaryFile(String ftpfile, String localfile)
        throws IOException, FtpException
    {
        if(out == null)
            return;
        getBinaryFile(ftpfile, localfile, 0, null);
    }

    /**
     * Read file from ftp server and write to a file in local hard disk.
     * This method is much faster than those method which return a byte array<br>
     * if the network is fast enough.<br>
     * <br>Remark:<br>
     * Cannot be used in unsigned applet.
     * @param ftpfile Name of file to be get from the ftp server, can be in full path.
     * @param localfile Name of local file to be write, can be in full path.
     * @param restart Restarting point
     * @exception FtpException if a ftp error occur. (eg. No such file in this case)
     * @exception IOException if an I/O error occur.
     */
    public void getBinaryFile(String ftpfile, String localfile, long restart)
        throws IOException, FtpException
    {
        if(out == null)
            return;
        getBinaryFile(ftpfile, localfile, restart, null);
    }

    /**
     * Read file from ftp server and write to a file in local hard disk.
     * This method is much faster than those method which return a byte array<br>
     * if the network is fast enough.<br>
     * <br>Remark:<br>
     * Cannot be used in unsigned applet.
     * @param ftpfile Name of file to be get from the ftp server, can be in full path.
     * @param localfile Name of local file to be write, can be in full path.
     * @param observer The FtpObserver which monitor this downloading progress
     * @exception FtpException if a ftp error occur. (eg. No such file in this case)
     * @exception IOException if an I/O error occur.
     * @see FtpObserver
     */
    public void getBinaryFile(String ftpfile, String localfile, FtpObserver observer)
        throws IOException, FtpException
    {
        if(out == null)
            return;
        getBinaryFile(ftpfile, localfile, 0, observer);
    }

    /**
     * Read from a ftp file and restart at a specific point.
     * This method is much faster than those method which return a byte array<br>
     * if the network is fast enough.<br>
     * Remark:<br>
     * Cannot be used in unsigned applet.
     * @param ftpfile Name of file to be get from the ftp server, can be in full path.
     * @param localfile File name of local file
     * @param restart Restarting point, ignored if equal or less than zero.
     * @param observer The FtpObserver which monitor this downloading progress
     * @exception FtpException if a ftp error occur. (eg. No such file in this case)
     * @exception IOException if an I/O error occur.
     * @see FtpObserver
     */
    public void getBinaryFile(String ftpfile, String localfile, long restart, FtpObserver observer)
        throws IOException, FtpException
    {
        if(out == null)
            return;

//        acquire();    // Acquire the object

        try
        {
            setTransferType(false);             // Set transfer type to binary
            Socket sock = null;
            sock = getDataSocket(CMD_RETR ,ftpfile, restart);
 
            // Read bytes from server and write to file.
            BufferedInputStream reader = new BufferedInputStream(
                                             sock.getInputStream());
            RandomAccessFile out = new RandomAccessFile(localfile, "rw");
            out.seek(restart);
            readData(reader, out, observer);
            reader.close();
            out.close();
            sock.close();
            getRespond(CMD_RETR);
        } finally
        {
//            release();    // Release the object
        }
    }
    
    
    /**
     * Put a binary file to the server from an array of byte.
     * @param filename The name of file.
     * @param content The byte array to be written to the server.
     * @exception FtpException if a ftp error occur. (eg. permission denied in this case)
     * @exception IOException if an I/O error occur.
     */
    public void putBinaryFile(String filename, byte[] content)
        throws IOException, FtpException
    {
        if(out == null)
            return;
        putBinaryFile(filename, content, -1);
    }

    /**
     * Put a binary file to the server from an array of byte with a restarting point
     * @param filename The name of file.
     * @param content The byte array to be write to the server.
     * @param restart The restarting point, ingored if less than or equal to zero.
     * @exception FtpException if a ftp error occur. (eg. permission denied in this case)
     * @exception IOException if an I/O error occur.
     */
    public void putBinaryFile(String filename, byte[] content, long restart)
        throws IOException, FtpException
    {
        if(out == null)
            return;
//        acquire();    // Acquire the object

        try
        {
            setTransferType(false);
            putFile(filename, content, restart);
        } finally{
 //           release();    // Release the object
        }
    }

    /**
     * Read a file from local hard disk and write to the server.
     * <br>Remark:<br>
     * <br>Cannot be used in unsigned applet.
     * @param local_file Name of local file, can be in full path.
     * @param remote_file Name of file in the ftp server, can be in full path.
     * @exception FtpException if a ftp error occur. (eg. permission denied)
     * @exception IOException if an I/O error occur.
     */
    public void putBinaryFile(String local_file, String remote_file)
        throws IOException, FtpException
    {
        if(out == null)
            return;
        putBinaryFile(local_file, remote_file, 0, null);
    }

    /**
     * Read a file from local hard disk and write to the server.
     * <br>Remark:<br>
     * <br>Cannot be used in unsigned applet.
     * @param local_file Name of local file, can be in full path.
     * @param remote_file Name of file in the ftp server, can be in full path.
     * @param observer The FtpObserver which monitor this uploading progress.
     * @exception FtpException if a ftp error occur. (eg. permission denied)
     * @exception IOException if an I/O error occur.
     */
    public void putBinaryFile(String local_file, String remote_file, FtpObserver observer)
        throws IOException, FtpException
    {
//	    System.out.println("putBinaryFile");
        if(out == null){
//        	System.out.println("putBinaryFile out == null");
            return;
        }
        putBinaryFile(local_file, remote_file, 0, observer);
    }

    /**
     * Read a file from local hard disk and write to the server with restarting point.
     * Remark:<br>
     * Cannot be used in unsigned applet.
     * @param local_file Name of local file, can be in full path.
     * @param remote_file Name of file in the ftp server, can be in full path.
     * @param restart The restarting point, ignored if less than or greater than zero.
     * @exception FtpException if a ftp error occur. (eg. permission denied)
     * @exception IOException if an I/O error occur.
     */
    public void putBinaryFile(String local_file, String remote_file, long restart)
        throws IOException, FtpException
    {
        if(out == null)
            return;
        putBinaryFile(local_file, remote_file, restart, null);
    }

    /**
        * Read a file from local hard disk and write to the server with restarting point.
     * Remark:<br>
     * Cannot be used in unsigned applet.
     * @param local_file Name of local file, can be in full path.
     * @param remote_file Name of file in the ftp server, can be in full path.
     * @param observer The FtpObserver which monitor this uploading progress
     * @exception FtpException if a ftp error occur. (eg. permission denied)
     * @exception IOException if an I/O error occur.
    */
    public void putBinaryFile(String local_file, String remote_file, long restart, FtpObserver observer)
        throws IOException, FtpException
    {
        if(out == null){
//	   	 System.out.println("out == null");
            return;
        }
//	    System.out.println("out != null");

//        acquire();    // Acquire the object

        try
        {
            Socket sock = null;
            setTransferType(false);
            RandomAccessFile fin = new RandomAccessFile(local_file, "r");
            sock = getDataSocket(CMD_STOR, remote_file, restart);
//			System.out.println("Socket:" + sock);

            if(restart > 0)
                fin.seek(restart);
            
            DataOutputStream dout = new DataOutputStream(sock.getOutputStream());
//            System.out.println("DataOutputStream:" + dout);
            
            writeData(fin, dout, observer);
            fin.close();
            dout.close();
            getRespond(CMD_STOR);
        } finally
        {
//            release();    // Release the object
        }
    }
    /**
     * Change directory.
     * @param directory Name of directory
     * @exception FtpException if a ftp error occur. (eg. permission denied in this case)
     * @exception IOException if an I/O error occur.
     */
    public void setDirectory(String directory)
        throws IOException, FtpException
    {
//	    System.out.println("setDirectory");
        if(out == null){
	        System.out.println("out == null");
            return;
        }

//        acquire();    // Acquire the object

        try
        {
            ftpCommand(CMD_CWD, directory);
        } finally
        {
//            release();    // Release the object
        }
//	    System.out.println("setDirectory");
    }

    /**
     * Change to parent directory.
     * @exception FtpException if a ftp error occur. (eg. permission denied in this case)
     * @exception IOException if an I/O error occur.
     */
    public void toParentDirectory()
        throws IOException, FtpException
    {
        if(out == null)
            return;

//        acquire();    // Acquire the object

        try
        {
            ftpCommand(CMD_CDUP);
        } finally
        {
 //           release();    // Release the object
        }
    }

    /**
     * Get the content of current directory
     * @return A FtpListResult object, return null if it is not connected.
     * @exception FtpException if a ftp error occur. (eg. permission denied in this case)
     * @exception IOException if an I/O error occur.
     * @see FtpListResult
     */
    public FtpListResult getDirectoryContent()
        throws IOException, FtpException
    {
        if(out == null)
            return null;
        String str_list = getDirectoryContentAsString();
        FtpListResult ftplist = new FtpListResult();
        ftplist.parseList(str_list, getSystemType());
        return ftplist;
    }
    /**
     * Get the type of operating system of the server.
     * Return null if it is not currently connected to any ftp server.
     * @return Name of the operating system.
     */
    public String getSystemType()
        throws IOException, FtpException
    {
        if(out == null)
            return null;
//        acquire();    // Acquire the object

        try
        {
            ftpCommand(CMD_SYST);
        } finally
        {
//            release();    // Release the object
        }

        return reply.substring(4);
    }

    /**
     * Get the content of current directory.
     * @return A list of directories, files and links in the current directory.
     * @exception FtpException if a ftp error occur. (eg. permission denied in this case)
     * @exception IOException if an I/O error occur.
     */
    public String getDirectoryContentAsString()
        throws IOException, FtpException
    {
        if(out == null)
            return null;

        StringBuffer list = new StringBuffer(""); // Directory list
        Socket sock = null;                       // Socket to establish data connection
//        acquire();    // Acquire the object

        try
        {
            // get DataSocket for the LIST command.
            // As no restarting point, send 0.
            sock = getDataSocket(CMD_LIST, 0);

            BufferedReader listen = new BufferedReader(
                                        new InputStreamReader(
                                            sock.getInputStream(), FTP_ENCODING));
            // Read bytes from server.
            String line;
            while((line = listen.readLine()) != null)
                list.append(line).append("\n");

            listen.close();
            sock.close();

            getRespond(CMD_LIST);
        } finally
        {
//            release();    // Release the object
        }
        return list.toString();
    }

    /**
     * Make a directory in the server.
     * @param directory The name of directory to be made.
     * @exception FtpException if a ftp error occur. (eg. permission denied in this case)
     * @exception IOException if an I/O error occur.
     */
    public void makeDirectory(String directory)
        throws IOException, FtpException
    {
        if(out == null)
            return;
//        acquire();    // Acquire the object

        try
        {
            ftpCommand(CMD_MKD ,directory);
        } finally
        {
//            release();    // Release the object
        }
    }

    /**
     * Remove a directory in the server
     * @param directory The name of directory to be removed
     * @exception FtpException if a ftp error occur. (eg. permission denied in this case)
     * @exception IOException if an I/O error occur.
     */
    public void removeDirectory(String directory)
        throws IOException, FtpException
    {
        if(out == null)
            return;
//        acquire();    // Acquire the object

        try
        {
            ftpCommand(CMD_RMD ,directory);
        } finally
        {
//            release();    // Release the object
        }
    }

    /**
     * Execute a command using ftp.
     * e.g. chmod 700 file
     * @param exec The command to execute.
     * @exception FtpException if a ftp error occur. (eg. command not understood)
     * @exception IOException if an I/O error occur.
     */
    public void execute(String exec)
        throws IOException, FtpException
    {
        if(out == null)
            return;
//        acquire();    // Acquire the object

        try
        {
            ftpCommand(CMD_SITE, exec);
        } finally
        {
//            release();    // Release the object
        }
    }
    
    /*
     * Read the data from the BufferedInputStream object and write to the RandomAccessFile object
     */
    private void readData(BufferedInputStream reader, RandomAccessFile out, FtpObserver observer)
        throws IOException
    {
        int offset;
        byte[] data = new byte[1024];
        while((offset = reader.read(data)) != -1)
        {
            out.write(data, 0, offset);
            if(observer != null)
                observer.byteRead(offset);
        }
    }
    
    /*
     * Write data from the RandomAccessFile object to a DataOutputStream object
     * @param din File to be read from local hard disk
     * @param dout Output stream to write content to the server.
     * @param observer FtpObserver of the uploading process.
     */
    private void writeData(RandomAccessFile din, DataOutputStream dout, FtpObserver observer)
        throws IOException
    {
        int offset;
        byte[] data = new byte[1024];
        while((offset = din.read(data)) != -1)
        {
            dout.write(data, 0, offset);

            if(observer != null)
                observer.byteWrite(offset);
        }
    }

    /**
     * Return the server name. Return "" if it is not connected to any server.
     */
    public String getServerName()
    {
        return server;
    }

    /**
     * Return the user name. Return "" if it is not connected to any server.
     */
    public String getUserName()
    {
        return user;
    }

    /**
     * Rename a file at the FTP server.
     * @param oldfilename The name of the file to be renamed
     * @param newfilename The new name of the file
     * @exception FtpException if a ftp error occur. (eg. A file named the new file name already in this case.)
     * @exception IOException if an I/O error occur.
     */
    public void fileRename(String oldfilename, String newfilename)
        throws IOException, FtpException
    {
        if(out == null)
            return;

//        acquire();    // Acquire this object

        try
        {
            ftpCommand(CMD_RNFR, oldfilename);
            ftpCommand(CMD_RNTO, newfilename);
        } finally
        {
 //           release();    // Release the object
        }
    }
    /**
     * Get current directory name.
     * @return The name of the current directory.
     * @exception FtpException if a ftp error occur.
     * @exception IOException if an I/O error occur.
     */
    public String getDirectory()
        throws IOException, FtpException
    {
        if(out == null)
            return null; 

//        acquire();    // Acquire the object

        try
        {
            ftpCommand(CMD_PWD);
        } finally
        {
//            release();    // Release the object
        }

        int first = reply.indexOf("\"");
        int last = reply.lastIndexOf("\"");
        return reply.substring(first + 1, last);
    }

    /**
     * Delete a file at the FTP server.
     * @param filename Name of the file to be deleted.
     * @exception FtpException if a ftp error occur. (eg. no such file in this case)
     * @exception IOException if an I/O error occur.
     */
    public void fileDelete(String filename)
        throws IOException, FtpException
    {
        if(out == null)
            return;

//        acquire();    // Acquire the object

        try
        {
	    ftpCommand(CMD_DELE, filename);
        } finally
        {
//            release();    // Release the object
        }    
    }
}

